/*************************************************************************
    > File Name: test.c
    > Author: baiy
    > Mail: baiyang0223@163.com 
    > Created Time: 2020-10-20-13:28:49
    > Func: 
 ************************************************************************/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>


// private header
#include <drm/drm_mm.h>


#include <linux/miscdevice.h>

#define DEV_NAME "MISC_DRMMM_TEST"
#define BUF_SIZE (0x10240)

struct test_info_t {
	void   *alloc_buf;
	struct drm_mm mm;
};

struct test_info_t * p_test_info;


#define DRM_TEST 1

static ssize_t misc_demo_read (struct file * filp, char __user * buf, size_t size, loff_t * offset)
{
	size_t ret;

	struct drm_mm_node *node = filp->private_data;
	pr_info("read: drm node is %px, start is %px, size is %llu\n", node,(void *)node->start, node->size);
#ifdef DRM_TEST
	ret = copy_to_user(buf, (void*)(node->start), size);
#else
	ret = copy_to_user(buf, p_test_info->alloc_buf,size)
#endif
	pr_info("read %lu byptes, ret is %lu\n",size,ret);
	if(ret > size ){
		return -1;
	} else {
		return size - ret;
	}
}
static ssize_t misc_demo_write (struct file * filp, const char __user * buf, size_t size, loff_t * offset)
{
	
	
	return 0;
}
static long misc_demo_ioctl (struct file * filp, unsigned int cmd, unsigned long args)
{
	return 0;
}
static int misc_demo_open (struct inode * inode, struct file * filp)
{
	struct drm_mm_node *node = kzalloc(sizeof(struct drm_mm_node), GFP_KERNEL);
	filp->private_data = node;
	memset(node, 0, sizeof(struct drm_mm_node));
#ifdef DRM_TEST
	drm_mm_insert_node(&p_test_info->mm, node, 1024);
#endif

	return 0;
}
static int misc_demo_release (struct inode * inode, struct file * filp)
{
	struct drm_mm_node *node = filp->private_data;
	if(node){
#ifdef DRM_TEST
		drm_mm_remove_node(node);
#endif
		kfree(node);
		filp->private_data = NULL;
	}
	return 0;
}




static struct file_operations misc_demo_fops = {
	.owner = THIS_MODULE,
	.open  = misc_demo_open,
	.release = misc_demo_release,
	.read  = misc_demo_read,
	.write = misc_demo_write,
	.unlocked_ioctl = misc_demo_ioctl,
	.compat_ioctl   = misc_demo_ioctl,
};

static struct miscdevice misc_demo_drv = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = DEV_NAME,
	.fops  = &misc_demo_fops,
};
static int __init test_init(void)
{
	pr_info("init [E]\n");
	p_test_info = kmalloc(sizeof(struct test_info_t), GFP_KERNEL);
	if( !p_test_info ){
		pr_err("alloc special struct failed.");
		return -1;
	}
	memset(p_test_info, 0, sizeof(struct test_info_t));
	p_test_info->alloc_buf = kmalloc(BUF_SIZE, GFP_KERNEL);
	if( !p_test_info->alloc_buf ) {
		pr_err("alloc special space failed.");
		goto err1;
	}
	memset(p_test_info->alloc_buf, 0x5a, BUF_SIZE);
#ifdef DRM_TEST
	drm_mm_init(&p_test_info->mm, (u64)p_test_info->alloc_buf, BUF_SIZE); // drm manager start 0x1024,size 0x2048
#endif

	misc_register(&misc_demo_drv);
	pr_info("manager space start 0x%px\n", p_test_info->alloc_buf);
	return 0;

err1:
	kfree(p_test_info);
	return -1;
}


static void __exit test_exit(void)
{
	pr_info("exit [E]\n");
#ifdef DRM_TEST
	drm_mm_takedown(&p_test_info->mm);
#endif
	if(p_test_info) {
		if(p_test_info->alloc_buf)
			kfree(p_test_info->alloc_buf);
		kfree(p_test_info);
	}
	misc_deregister(&misc_demo_drv);
}

module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
